#include "types.h" // uint64，PGSIZE
#include "trap.h" // tf, trap(), trap_ret_user()
#include "string.h" // memcpy()
#include "proc.h" // MAX_PROC_NUM
#include "pmm.h" //alloc_page()
#include "elf.h" //Elf64_Ehdr
#include "page.h"
#include "fs.h" // file_open()
#include "panic.h"
#include "stdio.h"

int app_num = 1;
static int app_idx = 0;
char *app_names[] = {"shell", "open", "read", "write", "clone", "execve", "exit", "fork", "wait", "waitpid",
		"getpid", "getppid"};

void app_init() {
	uint64 file_ptr;
	uint64 fsize;
	struct trapframe *tf_ptr;
	struct context *con_ptr;
	uint64 ustack;
	uint64 user_kstack;
	uint64 app_entry;
	struct proc *proc_ptr;
	struct pgtable_l3 *table_ptr3;
	int fd;
	// must initialize
	struct vma *vma_ptr = NULL;

	if (app_idx == app_num) {
		puts("All the user apps are over.\n");
		while(1);
	}
	// load app
		// create a new page table and map kernel codes
	table_ptr3 = (struct pgtable_l3 *)kalloc(sizeof(struct pgtable_l3));
	smode_map_pages(table_ptr3);
		// load file from disk
	if ((fd = fs_openat(AT_FDCWD, app_names[app_idx++], O_RDWR, O_RDWR)) == -1) {
		panic("app_init: open file failed.\n");
	}
	fsize = cur_proc->fd_table[fd]->size;
	file_ptr = kalloc(fsize);
	if (fs_read(fd, (void *)file_ptr, fsize) == -1) {
		panic("app_init: read file failed.\n");
	}
		// load elf file
	app_entry = load_elf((Elf64_Ehdr *)(file_ptr), table_ptr3, &vma_ptr);

	// free file
	kfree(file_ptr, fsize);
	fs_close(fd);
	// allocate user's stack and user's kernel stack
	ustack = kalloc(USTACK_SIZE);
	user_kstack = kalloc(PGSIZE);

	// create a new trapframe
	tf_ptr = (struct trapframe *)kalloc(sizeof(struct trapframe));
	// fill trapframe
	tf_ptr->epc = app_entry;
	// user stack top
	tf_ptr->sp = (uint64)ustack + PGSIZE;
	// kernel stack top
	tf_ptr->kstack = (uint64)user_kstack + PGSIZE;

	// create a new context
	con_ptr = (struct context *)kalloc(sizeof(struct context));
	con_ptr->ra = (uint64)trap_ret_user;
	con_ptr->sp = (uint64)user_kstack + PGSIZE;

	// allocate a struct proc
	proc_ptr = alloc_proc();
	proc_ptr->tf_ptr = tf_ptr;
	proc_ptr->con_ptr = con_ptr;
	proc_ptr->state = RUNNABLE;
	proc_ptr->table_ptr3 = table_ptr3;
	proc_ptr->vma_list = vma_ptr;
	proc_ptr->vma_list->count++;
	proc_ptr->parent_ptr = init_proc;
	proc_ptr->zombie_child_list = NULL;

	// init fd_table
	init_fd_table(proc_ptr->fd_table);

	run_proc(proc_ptr);
}
